package evthub

import (
	"fmt"
	"sync"
	"sync/atomic"
)

type EvtFunc func(args ...interface{}) (ok bool)

type idfunc struct {
	id int32
	fn EvtFunc
}

var (
	idhandle int32
)

// 事件节点
type EvtNode struct {
	refcnt  int32
	id      string
	lk      sync.RWMutex
	evtlst  []*idfunc
	execlst []*idfunc
}

// 事件中心
// 负责管理事件节点对象
// 使用方式1: A对象: 创建EvtNode, 执行事件, 释放EvtNode, (B/C/D)对象: 绑定事件 -> 解绑事件
// 使用方式2: (A/B/C/D): CheckGetEvtNode('001', true)获取节点, (node.绑定事件 -> node.解绑事件) / (执行事件) -> 释放EvtNode,
// 使用方式3: (A/B/C/D): CheckGetEvtNode('001', true), AddEvt(绑定事件) ,... ,  DelEvt, ReleaseEvtNode
type EvtHub struct {
	lk     sync.RWMutex
	lstMap map[string]*EvtNode
}

var (
	DefaultEvtHub *EvtHub
)

func NewEvtHub() *EvtHub {
	rval := &EvtHub{
		lstMap: make(map[string]*EvtNode),
	}
	return rval
}

func init() {
	DefaultEvtHub = NewEvtHub()
}

func (this *EvtNode) GetId() string {
	return this.id
}

func (this *EvtNode) Add2Evt(fn EvtFunc) (evtid int32) {
	id := atomic.AddInt32(&idhandle, 1)
	this.lk.Lock()
	defer this.lk.Unlock()
	this.evtlst = append(this.evtlst, &idfunc{id, fn})
	this.execlst = make([]*idfunc, len(this.evtlst))
	copy(this.execlst, this.evtlst)
	return id
}

func (this *EvtNode) Execute(max int, args ...interface{}) (cnt int) {
	lst := this.execlst
	for _, s := range lst {
		if s.fn(args...) {
			cnt++
		}
		if max > 0 && cnt >= max {
			break
		}
	}
	return
}

func (this *EvtNode) RemoveEvt(evtid int32) (changed bool) {
	this.lk.Lock()
	defer this.lk.Unlock()
	for i, f := range this.evtlst {
		if f.id == evtid {
			this.evtlst = append(this.evtlst[:i], this.evtlst[i+1:]...)
			changed = true
		}
	}
	if changed {
		this.execlst = make([]*idfunc, len(this.evtlst))
		copy(this.execlst, this.evtlst)
	}
	return
}

func (this *EvtHub) CheckGetEvtNode(id string, newflag bool) (node *EvtNode) {
	this.lk.RLock()
	node = this.lstMap[id]
	if node != nil {
		atomic.AddInt32(&node.refcnt, 1)
	}
	this.lk.RUnlock()
	if node == nil && newflag {
		this.lk.Lock()
		defer this.lk.Unlock()
		node = this.lstMap[id]
		if node == nil {
			node = &EvtNode{id: id, evtlst: make([]*idfunc, 0)}
			this.lstMap[id] = node
		}
		atomic.AddInt32(&node.refcnt, 1)
	}
	return node
}

// 添加事件, 事件节点引用计数+1
func (this *EvtHub) AddEvent(nodeid string, evt EvtFunc) (evtid int32) {
	this.lk.RLock()
	defer this.lk.RUnlock()
	node := this.lstMap[nodeid]
	if node == nil {
		return -1
	}
	atomic.AddInt32(&node.refcnt, 1)
	return node.Add2Evt(evt)
}

// 删除事件, 事件节点引用计数-1
func (this *EvtHub) DelEvent(nodeid string, evtid int32) (changed bool) {
	this.lk.RLock()
	node := this.lstMap[nodeid]
	this.lk.RUnlock()
	if node == nil {
		return false
	}
	if node.RemoveEvt(evtid) { // 存在
		refcnt := atomic.AddInt32(&node.refcnt, -1)
		if refcnt == 0 {
			this.innerCheckRemove(node, nodeid)
		}
		changed = true
	}
	return
}

func (this *EvtHub) CreateEvtNode(id string) (*EvtNode, error) {
	this.lk.RLock()
	node := this.lstMap[id]
	if node != nil {
		atomic.AddInt32(&node.refcnt, 1)
	}
	this.lk.RUnlock()
	if node != nil {
		return nil, fmt.Errorf("[%s]已经存在", id)
	}

	this.lk.Lock()
	defer this.lk.Unlock()
	node = this.lstMap[id]
	if node == nil {
		node = &EvtNode{id: id, evtlst: make([]*idfunc, 0)}
		atomic.AddInt32(&node.refcnt, 1)
		this.lstMap[id] = node
		return node, nil
	} else {
		return nil, fmt.Errorf("[%s]已经存在", id)
	}
}

func (this *EvtHub) innerCheckRemove(node *EvtNode, id string) bool {
	this.lk.Lock()
	defer this.lk.Unlock()
	refcnt := atomic.LoadInt32(&node.refcnt)
	if refcnt == 0 {
		delete(this.lstMap, id)
		return true
	}
	return false
}

func (this *EvtHub) ReleasesEvtNode(id string) bool {
	var refcnt int32
	this.lk.RLock()
	node := this.lstMap[id]
	if node != nil {
		refcnt = atomic.AddInt32(&node.refcnt, -1)
	}
	this.lk.RUnlock()
	if node == nil {
		return false
	}
	if refcnt == 0 {
		return this.innerCheckRemove(node, id)
	}
	return false
}
